//---------------------------------------------------------------------------
// <copyright file="RibbonDropDownButton.cs" company="Microsoft Corporation">
//     Copyright (C) Microsoft Corporation.  All rights reserved.
// </copyright>
//---------------------------------------------------------------------------

namespace Microsoft.Windows.Controls.Ribbon
{
    #region Using declarations

    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Controls.Primitives;
    using System.Windows.Data;
    using System.Windows.Input;

    #endregion

    /// <summary>
    ///   RibbonDropDownButton inherits from MenuBase so that it can contain MenuItems, and implements
    ///   IRibbonControl so that it can be placed in the Ribbon.  It also implements ICommandSource so
    ///   that it has the ability to execute RibbonCommands via its items, and ToolTip property coercion
    ///   to allow use of Ribbon tooltips.
    /// </summary>
    public class RibbonDropDownButton : MenuBase, IRibbonControl, ICommandSource
    {
        #region Constructors

        /// <summary>
        ///   Initializes static members of the RibbonDropDownButton class.  Here we override the default
        ///   style, a coerce callback, and allow tooltips to be shown for disabled commands.
        /// </summary>
        static RibbonDropDownButton()
        {
            Type ownerType = typeof(RibbonDropDownButton);

            DefaultStyleKeyProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(ownerType));
            ToolTipProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(null, new CoerceValueCallback(CoerceToolTip)));
            ToolTipService.ShowOnDisabledProperty.OverrideMetadata(ownerType, new FrameworkPropertyMetadata(true));
        }
        
        #endregion

        #region Public Properties

        /// <summary>
        ///   We cache a reference to the popup when it is open so that we can close it when
        ///   focus is lost.
        /// </summary>
        private Popup _popup;

        /// <summary>
        ///   Gets or sets the Command that will be executed when the command source is invoked.
        /// </summary>
        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for CommandProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CommandProperty =
                    DependencyProperty.Register(
                            "Command",
                            typeof(ICommand),
                            typeof(RibbonDropDownButton),
                            new FrameworkPropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));

        /// <summary>
        ///   Gets or sets a user defined data value that can be passed to the command when it is executed.
        /// </summary>
        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for CommandParameterProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty =
                    DependencyProperty.Register(
                            "CommandParameter",
                            typeof(object),
                            typeof(RibbonDropDownButton),
                            new FrameworkPropertyMetadata(null));

        /// <summary>
        ///   Gets or sets the object that the command is being executed on.
        /// </summary>
        public IInputElement CommandTarget
        {
            get { return (IInputElement)GetValue(CommandTargetProperty); }
            set { SetValue(CommandTargetProperty, value); }
        }

        /// <summary>
        ///   Using a DependencyProperty as the backing store for CommandTargetProperty.  This enables animation, styling, binding, etc...
        /// </summary>
        public static readonly DependencyProperty CommandTargetProperty =
                    DependencyProperty.Register(
                            "CommandTarget",
                            typeof(IInputElement),
                            typeof(RibbonDropDownButton),
                            new FrameworkPropertyMetadata(null));

        #endregion

        #region Public Methods

        /// <summary>
        ///   If the developer hasn't assigned their own tooltip, Create and attach a new RibbonToolTip.
        /// </summary>
        /// <param name="d">
        ///   The RibbonDropDownButton that the ToolTip property exists on.  When the callback is invoked,
        ///   the property system will pass this value.
        /// </param>
        /// <param name="value">The new value of the ToolTip property, prior to any coercion attempt.</param>
        /// <returns>The coerced value of the ToolTip property.</returns>
        public static object CoerceToolTip(DependencyObject d, object value)
        {
            if (value == null)
            {
                RibbonDropDownButton dropDownButton = (RibbonDropDownButton)d;
                RibbonCommand command = dropDownButton.Command as RibbonCommand;
                if (command != null &&
                    (!string.IsNullOrEmpty(command.ToolTipTitle) || !string.IsNullOrEmpty(command.ToolTipDescription) || command.ToolTipImageSource != null))
                {
                    object toggleButton = dropDownButton.GetTemplateChild("PART_ToggleButton");
                    if (toggleButton is RibbonToggleButton)
                    {
                        value = new RibbonToolTip(command);
                    }
                }
            }

            return value;
        }

        /// <summary>
        ///   Invoked whenever the control's template is applied.  Here we cache a reference
        ///   to the RibbonDropDownButton's popup, update its tooltip and its internal
        ///   RibbonToggleButton.
        /// </summary>
        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            _popup = GetTemplateChild("PART_Popup") as Popup;
            this.UpdateToolTip();
            this.UpdateToggleButtonCommand();
        }

        #endregion

        #region Protected Methods
        
        /// <summary>
        ///   Invoked whenever the RibbonDropDownButton loses mouse capture.  In this
        ///   event hide the RibbonDropDownButton's popup.
        /// </summary>
        /// <param name="e">The MouseEventArgs that contains event data.</param>
        protected override void OnLostMouseCapture(MouseEventArgs e)
        {
            base.OnLostMouseCapture(e);

            object oldCapture = e.OriginalSource;
            object newCapture = Mouse.Captured;

            if (oldCapture == this && newCapture != this)
            {
                if (newCapture == null)
                {
                    _popup.IsOpen = false;
                }
            }
        }

        #endregion

        #region Private Methods

        /// <summary>
        ///   If the Command property changes, we need to coerce some RibbonControl
        ///   specific properties to update their 'back-up' values coming from Command.
        /// </summary>
        /// <param name="d">The RibbonDropDownButton on which the Command property has changed value.</param>
        /// <param name="e">
        ///   Event data that is issued by any event that tracks changes to the effective value of this property.
        /// </param>
        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            RibbonDropDownButton menu = (RibbonDropDownButton)d;
            menu.UpdateToolTip();
            menu.UpdateToggleButtonCommand();
        }

        /// <summary>
        ///   Coerces the value of the ToolTip, and adds a binding to the ToolTips visibility
        ///   property so that it is only visible when the RibbonDropDownButton's items
        ///   are not being displayed.
        /// </summary>
        private void UpdateToolTip()
        {
            CoerceValue(ToolTipProperty);

            Binding binding = new Binding("IsChecked");
            binding.Source = GetTemplateChild("PART_ToggleButton");
            binding.Converter = new RibbonBooleanToVisibilityConverter();

            RibbonToolTip toolTip = ToolTip as RibbonToolTip;
            if (toolTip != null)
            {
                toolTip.SetBinding(RibbonToolTip.VisibilityProperty, binding);
            }
        }

        /// <summary>
        ///   RibbonDropDownButton is templated with an internal RibbonToggleButton, this
        ///   method updates the properties on that internal RibbonToggleButton's Command.
        /// </summary>
        private void UpdateToggleButtonCommand()
        {
            RibbonToggleButton toggle = (RibbonToggleButton)GetTemplateChild("PART_ToggleButton");
            RibbonCommand command = this.Command as RibbonCommand;
            if (toggle != null && command != null)
            {
                RibbonCommand intermediateCommand = new RibbonCommand()
                {
                    LargeImageSource = command.LargeImageSource,
                    SmallImageSource = command.SmallImageSource,
                    LabelTitle = command.LabelTitle,
                };
                intermediateCommand.CanExecute += new CanExecuteRoutedEventHandler(this.IntermediateCommand_CanExecute);

                toggle.Command = intermediateCommand;
            }
        }

        /// <summary>
        ///   A CanExecute callback for the internally held RibbonToggleButton which returns
        ///   true if the RibbonDropDownButton contains items.
        /// </summary>
        /// <param name="sender">The command target that is invoking the handler.</param>
        /// <param name="e">The event data.</param>
        private void IntermediateCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            if (Items.Count > 0)
            {
                e.CanExecute = true;
            }
        }

        #endregion
    }
}